第 14 天,我示範了host element
可以將properties
和attributes
綁定到signal
和signal input
。signal input
也可以綁定到host directive
的host properties
,就像它是組件的一部分一樣。
在我的例子中,我展示了 Directive Composition API
如何將signal
和signal input
綁定到host directive
並更新組件的 CSS 樣式。
import { Component, signal } from '@angular/core';
import { filter, fromEvent, map } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
import KeyComponent from './key.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [KeyComponent],
template: `
<h1>Hello from {{ name }}!</h1>
<h2>{{ description }}</h2>
<div class="container">
@for(v of keys(); track v) {
@let isPressed = key() === v;
@let bgColor = isPressed ? 'yellow' : 'white';
<app-key [value]="v" [isPressed]="isPressed"
[ariaLabel]="v + ' key'" [bgColor]="bgColor"
/>
}
</div>
`,
})
export class App {
name = 'iTHome Ironman 2024 day 15';
description = 'Bind signal inputs to Directive Composition API';
keys = signal('asdfghjkl');
key = toSignal(fromEvent(document, 'keyup').pipe(
filter((evt) => evt instanceof KeyboardEvent),
map((evt) => evt as KeyboardEvent),
map((evt) => evt.key.toLowerCase()),
filter((key) => 'asdfghjkl'.indexOf(key) >= 0)
), { initialValue: '' });
}
App
組件顯示九個分別帶有字母 a、s、d、f、g、h、j、k、 l 的方塊。 當使用者按下上述任何鍵時,對應的方塊會變更 CSS 樣式。此組件偵聽 keyup
事件並呼叫 toSignal
以建立 key
signal。 signal
值綁定到 KeyComponent
的 isPressed
input。當字母與按下的鍵相符時,bgColor
也具有不同的背景顏色。我使用 Angular 18 的 let
語法來計算 expression 並將結果指派給 local template variable。
import { computed, Directive, input } from "@angular/core";
@Directive({
selector: '[appKeyPressed]',
standalone: true,
host: {
'[style.background-color]': 'bgColor()',
'[class.hit]': 'isPressed()',
'[attr.label]': 'ariaLabel()',
'[style]': 'styleObject()',
},
})
export class KeyPressedDirective {
value = input.required<string>();
isPressed = input.required<boolean>();
ariaLabel = input.required<string>();
bgColor = input.required<'yellow' | 'white'>();
styleObject = computed(() => {
const fontSize = this.isPressed() ? 2.5 : 2;
const color = this.isPressed() ? 'cyan' : 'black';
return {
fontSize: `${fontSize}rem`,
color,
}
});
}
KeyPressedDirective
directive有四個 signal inputs
和一個 computed signal
。 background-color
style 屬性綁定到黃色或白色的 bgColor
input。 Host class, hit, 綁定到 isPressed
input 以變更邊框顏色、邊框寬度和邊框半徑。 label attribute 綁定到 ariaLabel
input 並顯示 "{letter} key"。當您檢查host element
時,會看到 label
attribute。 style
物件綁定到 styleObject
computed signal 以更改文字顏色並增加字體大小。
@Component({
selector: 'app-key',
standalone: true,
hostDirectives: [
{
directive: KeyPressedDirective,
inputs: ['value', 'isPressed', 'bgColor', 'ariaLabel']
}
],
})
KeyComponent
組件在 hostDirective
屬性中註冊 KeyPressedDirective
directive。 host
有四個 inputs,分別是 value
、 isPressed
、 bgColor
和 ariaLabel
。
styles: `
:host {
display: flex;
justify-content: center;
border: 1px solid black;
width: 100px;
height: 100px;
}
:host-context(.hit) {
border-color: rebeccapurple;
border-width: 0.4rem;
border-radius: 0.5rem;
}`
host element
將字母放置在 flex container
的中心。 hit
class 更改邊框顏色、邊框寬度和邊框半徑。
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { KeyPressedDirective } from './key-pressed.directive';
@Component({
selector: 'app-key',
standalone: true,
template: `
<div>
<span>{{ directive.value() }}</span>
</div>
`,
})
export default class KeyComponent {
directive = inject(KeyPressedDirective);
}
KeyComponent
組件注入 KeyPressedDirective
指令 , 並在 HTML 範本中顯示 signal input。
import { Component, ChangeDetectionStrategy, inject, input } from '@angular/core';
import { KeyPressedDirective } from './key-pressed.directive';
@Component({
selector: 'app-photo',
standalone: true,
template: `
<div class="photo">
<img [src]="img()" [alt]="directive.ariaLabel()" />
<p>{{ directive.value() }}</p>
</div>
`,
hostDirectives: [
{
directive: KeyPressedDirective,
inputs: ['value', 'isPressed', 'bgColor', 'ariaLabel:alt']
}
],
styles: `
:host {
display: flex;
justify-content: center;
border: 0.6rem solid black;
Border-radius: 0.75rem;
}
:host-context(.hit) {
border-color: orange;
border-style: dashed
} `,
})
export default class PhotoComponent {
directive = inject(KeyPressedDirective);
img = input.required<string>();
}
在 PhotoComponent
組件中重複使用 KeyPressedDirective
指令。此組件注入 host directive
,並顯示值和圖像替代文字 (image alt text)。
// main.ts
<div class="photo">
<app-photo [alt]="alt()" [value]="photoDescription()"
[bgColor]="bgColor()" [isPressed]="isOn()" [img]="img()" />
<button (click)="toggle()">Change</button>
</div>
const base = 'https://picsum.photos/200?random='
alt = signal('A random picture');
photoDescription = signal('Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis tincidunt nibh. Fusce euismod scelerisque est sit amet sagittis. Nulla.');
isOn = signal(false);
img = signal(`${base}1`);
bgColor = computed(() => this.isOn() ? 'yellow' : 'white');
toggle() {
this.isOn.update((prev) => !prev);
this.img.set(`${base}${Date.now()}`);
}
將 PhotoComponent
組件匯入到 App
組件中。當您按一下 Change
按鈕時,isOn
signal 會切換其值,img
signal 會設定新的 URL
, bgColor
signal 會重新計算背景顏色。 當發生 change detection
時,PhotoComponent
組件會更新圖片、背景顏色和邊框樣式。
Directive Composition API
允許 directive
成為組件的一部分。Host directive
可以將屬性和屬性綁定到signal
和 signal input
。當 signal input
更新時, host element
也會更新。host directive
來存取其 signal inputs
。Host directive
可以在許多組件中註冊以供重複使用。鐵人賽的第15天就這樣結束了。
-- Composition API Documentation: https://angular.dev/guide/directives/directive-composition-api